home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / zansi_12.arc / ZANSI.ASM < prev    next >
Assembly Source File  |  1987-07-18  |  32KB  |  929 lines

  1. ;--- zansi.asm ----------------------------------------------------------
  2. ; Zephyr ANSI terminal driver.
  3. ;    Copyright (C) 1986-1987, Thomas Hanlin III, Alexandria VA.
  4. ;    Based on original code for NANSI by Daniel Kegel, Pasadena CA.
  5. ;------------------------------------------------------------------------
  6.  
  7.         ; from zansi_f.asm
  8.         extrn   f_escape:near, f_in_escape:near
  9.  
  10.         ; from zansi_p.asm
  11.         extrn   param_end:word
  12.  
  13.         ; from zansi_i.asm
  14.         extrn   dosfn0:near
  15.  
  16.         ; to zansi_p.asm
  17.         public  f_loopdone
  18.         public  f_not_ansi
  19.         public  f_ansi_exit
  20.  
  21.         ; to both zansi_p.asm and zansi_f.asm
  22.         public  cur_x, cur_y, max_x, cur_attrib
  23.  
  24.         ; to zansi_f.asm
  25.         public  xy_to_regs, get_blank_attrib
  26.         public  port_6845
  27.         public  wrap_flag
  28.         public  cur_parm_ptr
  29.         public  cur_coords, saved_coords, max_y
  30.         public  escvector, string_term
  31.         public  cpr_esc, cprseq
  32.         public  video_mode
  33.  
  34.         ; to zansi_i.asm
  35.         public  req_ptr, break_handler
  36.         public  int_29
  37.  
  38.         ; to all modules
  39.  
  40. keybuf  struc                           ; Used in getchar
  41. len     dw      ?
  42. adr     dw      ?
  43. keybuf  ends
  44.  
  45.  
  46. ABS40   segment at 40h
  47.         org     1ah
  48. buffer_head     dw      ?       ; Used in 'flush input buffer' dos call.
  49. buffer_tail     dw      ?
  50.  
  51.         org     49h
  52. crt_mode        db      ?
  53. crt_cols        dw      ?
  54. crt_len         dw      ?
  55. crt_start       dw      ?
  56. cursor_posn     dw      8 dup (?)
  57. cursor_mode     dw      ?
  58. active_page     db      ?
  59. addr_6845       dw      ?
  60. crt_mode_set    db      ?       ; = 7 only if monochrome display adaptor
  61. crt_palette     db      ?
  62.         org     6ch
  63. timer_low       dw      ?       ; low word of time-of-day counter (18.2 hz)
  64.  
  65. ABS40   ends
  66.  
  67.  
  68. CODE    segment byte public 'CODE'
  69. assume  cs:code, ds:code
  70.  
  71.         ; Device Driver Header
  72.  
  73.         org     0
  74.  
  75.         dd      -1                      ; next device
  76.         dw      8013h                   ; attributes
  77.         dw      strategy                ; request header pointer entry
  78.         dw      interrupt               ; request entry point
  79.         db      'CON', 5 dup(' ')       ; device name (8 char)
  80.  
  81.  
  82. ;----- variable area --------------------
  83. req_ptr label   dword
  84. req_off dw      ?
  85. req_seg dw      ?
  86.  
  87. wrap_flag       db      1       ; 0 = no wrap past line end
  88. escvector       dw      0       ; state vector of ESCape sequencer
  89. video_mode      db      3       ; ROM BIOS video mode (2=BW, 3=color)
  90. max_y           db      24
  91. max_cur_x       label   word    ; used to get both max & cur at once
  92. max_x           db      79      ; line width (79 for 80x25 modes)
  93. cur_coords      label   word
  94. cur_x           db      0       ; cursor position (0 = left edge)
  95. cur_y           db      0       ;                 (0 = top edge)
  96. saved_coords    dw      ?       ; holds XY after a SCP escape sequence
  97. string_term     db      0       ; either escape or double quote
  98. cur_attrib      db      7       ; current char attributes
  99. cur_page        db      0       ; current display page
  100. video_seg       dw      ?       ; segment of video card
  101. f_cptr_seg      dw      ?       ; part of fastout write buffer pointer
  102. cur_parm_ptr    dw      ?       ; last byte of parm area now used
  103. port_6845       dw      ?       ; port address of 6845 card
  104.  
  105. brkkeybuf       db      3       ; control C
  106. fnkeybuf        db      ?       ; holds second byte of fn key codes
  107. cpr_buf         db      8 dup (?), '['
  108. cpr_esc         db      27      ; descending buffer for cpr function
  109.  
  110. ; following four keybufs hold information about input
  111. ; Storage order determines priority- since the characters making up a function
  112. ; key code must never be separated (say, by a Control-Break), they have the
  113. ; highest priority, and so on.  Keyboard keys (except ctrl-break) have the
  114. ; lowest priority.
  115.  
  116. fnkey   keybuf  <0, fnkeybuf>   ; fn key string (0 followed by scan code)
  117. cprseq  keybuf  <0>             ; CPR string (ESC [ y;x R)
  118. brkkey  keybuf  <0, brkkeybuf>  ; ^C
  119. xlatseq keybuf  <0>             ; keyboard reassignment string
  120.  
  121. ;------ xy_to_regs --------------------------------------------
  122. ; on entry: x in cur_x, y in cur_y
  123. ; on exit:  dx = chars left on line, di = address
  124. ; Alters ax, bx.
  125. xy_to_regs      proc    near
  126.         ; Find number of chars til end of line, keep in DX
  127.         mov     ax,max_cur_x
  128.         mov     bl,ah
  129.         xor     bh,bh
  130.         cbw
  131.         mov     dx,ax
  132.         sub     dx,bx
  133.         inc     dx                      ; DX is # of chars till EOL
  134.         ; Calculate DI = current address in text buffer
  135.         inc     ax                      ; AL = max_x
  136.         mul     cur_y
  137.         add     ax,bx                   ; AX is # of chars into buffer
  138.         shl     ax,1
  139.         mov     di,ax                   ; DI is now offset of cursor.
  140.         ret
  141. xy_to_regs      endp
  142.  
  143.  
  144. ;------- dos_fn_tab -------------
  145. ; This table is used in "interrupt" to call the routine that handles
  146. ; the requested function.
  147.  
  148. max_cmd equ     12
  149. dos_fn_tab:
  150.         dw      dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  151.         dw      dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  152.  
  153. ;------- strategy ----------------------------------------------------
  154. ; DOS calls strategy with a request which is to be executed later.
  155. ; Strategy just saves the request.
  156.  
  157. strategy        proc    far
  158.         mov     cs:req_off,BX
  159.         mov     cs:req_seg,ES
  160.         ret
  161. strategy        endp
  162.  
  163. ;------ interrupt -----------------------------------------------------
  164. ; This is where the request handed us during "strategy" is
  165. ; actually carried out.
  166. ; Calls one of 12 subroutines depending on the function requested.
  167. ; Each subroutine returns with exit status in AX.
  168.  
  169. interrupt       proc    far
  170.         sti
  171.         push    ax
  172.         push    cx
  173.         push    dx
  174.         push    bx
  175.         push    bp
  176.         push    si
  177.         push    di
  178.         push    ds
  179.         push    es
  180.  
  181.         ; Read requested function information into registers
  182.         lds     bx,cs:req_ptr
  183.         mov     al,2[BX]                ; al = function code
  184.         les     si,14[BX]               ; ES:SI = input/output buffer addr
  185.         mov     cx,18[BX]               ; cx = input/output byte count
  186.  
  187.         cmp     al,max_cmd
  188.         ja      unk_command             ; too big, exit with error code
  189.  
  190.         mov     bx,ax
  191.         shl     bx,1                    ; form index to table of words
  192.         mov     ax,cs
  193.         mov     ds,ax
  194.         call    word ptr dos_fn_tab[bx]
  195. int_done:
  196.         lds     bx,cs:req_ptr           ; report status
  197.         or      ax,100h                 ; (always set done bit upon exit)
  198.         mov     3[bx],ax
  199.  
  200.         pop     ES                      ; restore caller's registers
  201.         pop     DS
  202.         pop     di
  203.         pop     si
  204.         pop     bp
  205.         pop     bx
  206.         pop     dx
  207.         pop     cx
  208.         pop     ax
  209.         ret                             ; return to DOS.
  210.  
  211. unk_command:
  212.         call    badcmd
  213.         jmp     int_done
  214.  
  215. interrupt       endp
  216.  
  217. ;----- BIOS break handler -----------------------------------------
  218. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  219. ; Simply notes that a break was hit.  Flag is checked during input calls.
  220.  
  221. break_handler   proc
  222.         mov     cs:brkkey.len, 1
  223.         iret
  224. break_handler   endp
  225.  
  226.  
  227. ;------ badcmd -------------------------------------------------------
  228. ; Invalid function request by DOS.
  229. badcmd  proc    near
  230.         mov     ax, 813h                ; return "Error: invalid cmd"
  231.         ret
  232. badcmd  endp
  233.  
  234.  
  235. ;------ nopcmd -------------------------------------------------------
  236. ; Unimplemented or dummy function request by DOS.
  237. nopcmd  proc    near
  238.         xor     ax, ax                  ; No error, not busy.
  239.         ret
  240. nopcmd  endp
  241.  
  242. ;------- dos function #4 -----------------------------------------------
  243. ; Reads CX characters from the keyboard, places them in buffer at ES:SI.
  244. dosfn4  proc    near
  245.         jcxz    dos4done
  246.         mov     di,si
  247. dos4lp: push    cx
  248.         call    getchar
  249.         pop     cx
  250.         stosb
  251.         loop    dos4lp
  252. dos4done:
  253.         xor     ax,ax                   ; No error, not busy.
  254.         ret
  255. dosfn4  endp
  256.  
  257. ;-------- dos function #5: non-destructive input, no wait ------
  258. ; One-character lookahead into the keyboard buffer.
  259. ; If no characters in buffer, return BUSY; otherwise, get value of first
  260. ; character of buffer, stuff into request header, return DONE.
  261. dosfn5  proc    near
  262.         call    peekchar
  263.         jz      dos5_busy
  264.         lds     bx,req_ptr
  265.         mov     [bx+0Dh], al
  266.         xor     ax, ax                  ; No error, not busy.
  267.         ret
  268. dos5_busy:
  269.         mov     ax, 200h                ; No error, busy.
  270.         ret
  271.  
  272. dosfn5  endp
  273.  
  274. ;-------- dos function #6: input status --------------------------
  275. ; Returns "busy" if no characters waiting to be read.
  276. dosfn6  proc    near
  277.         call    peekchar
  278.         mov     ax, 200h                ; No error, busy.
  279.         jz      dos6_exit
  280.         xor     ax, ax                  ; No error, not busy.
  281. dos6_exit:
  282.         ret
  283. dosfn6  endp
  284.  
  285. ;-------- dos function #7: flush input buffer --------------------
  286. ; Clears the IBM keyboard input buffer.  Since it is a circular
  287. ; queue, we can do this without knowing the beginning and end
  288. ; of the buffer; all we need to do is set the tail of the queue
  289. ; equal to the head (as if we had read the entire queue contents).
  290. ; Also resets all the device driver's stuffahead buffers.
  291. dosfn7  proc    near
  292.         mov     ax, abs40
  293.         mov     es, ax
  294.         mov     ax, es:buffer_head      ; clear queue by making the tail
  295.         mov     es:buffer_tail, ax      ; equal to the head
  296.         xor     ax, ax                  ; no error, not busy
  297.         mov     fnkey.len, ax           ; Reset the stuffahead buffers.
  298.         mov     cprseq.len, ax
  299.         mov     brkkey.len, ax
  300.         mov     xlatseq.len, ax
  301.         ret
  302. dosfn7  endp
  303.  
  304. ;------ int_29 ----------------------------------------------
  305. ; Int 29 handles DOS quick-access putchar.
  306. ; Last device loaded with attribute bit 4 set gets accessed for
  307. ; single-character writes via int 29h instead of via interrupt.
  308. ; Must preserve all registers.
  309. ; Installed as int 29h by dosfn0 (init).
  310. int_29_buf      db      ?
  311.  
  312. int_29  proc    near
  313.         sti
  314.         push    ds
  315.         push    es
  316.         push    ax
  317.         push    cx
  318.         push    dx
  319.         push    bx
  320.         push    bp
  321.         push    si
  322.         push    di
  323.         mov     cx,1
  324.         mov     bx,cs
  325.         mov     es,bx
  326.         mov     ds,bx
  327.         mov     si,offset int_29_buf
  328.         mov     [si],al
  329.         call    dosfn8
  330.         pop     di
  331.         pop     si
  332.         pop     bp
  333.         pop     bx
  334.         pop     dx
  335.         pop     cx
  336.         pop     ax
  337.         pop     es
  338.         pop     ds
  339.         iret
  340. int_29  endp
  341.  
  342. ;------ dosfn8 -------------------------------------------------------
  343. ; Handles writes to the device (with or without verify).
  344. ; Called with
  345. ;  CX    = number of bytes to write
  346. ;  ES:SI = transfer buffer
  347. ;  DS    = CS, so we can access local variables.
  348.  
  349. dosfn8  proc    near
  350.  
  351.         mov     f_cptr_seg, es  ; save segment of char ptr
  352.  
  353.         ; Read the BIOS buffer address/cursor position variables.
  354.         mov     ax,abs40
  355.         mov     ds,ax
  356.         assume  ds:abs40
  357.  
  358.         ; Find current video mode and screen size.
  359.         mov     ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  360.         mov     cs:video_mode, al
  361.         dec     ah                      ; ah = max column
  362.         mov     cs:max_x, ah
  363.  
  364.         ; Find current cursor coordinates.
  365.         mov     al,active_page
  366.         cbw
  367.         shl     ax,1
  368.         mov     bx,ax
  369.         mov     ax,cursor_posn[bx]
  370.         mov     cs:cur_coords,AX
  371.  
  372.         ; Find video buffer segment address; adjust so ofs is 0; return in AX.
  373.         ; DS is abs40.
  374.         mov     ax,addr_6845            ; 6845 address
  375.         mov     cs:port_6845,ax
  376.  
  377.         mov     ax,cs
  378.         mov     ds,ax
  379.         assume  ds:code
  380.  
  381.         mov     ax,0B000h               ; assume it's a monochrome card...
  382.         cmp     video_mode,7
  383.         jz      d8_gots
  384.         mov     ah,0B8h                 ; but if not mode 7, it's color.
  385. d8_gots:
  386.         mov     video_seg,ax
  387.         mov     es,ax
  388.         call    xy_to_regs              ; Set DX, DI according to cur_coords.
  389.  
  390.         ; | If in graphics mode, clear old pseudocursor
  391.         cmp     cs:video_mode, 4
  392.         jb      d8_no_cp
  393.         cmp     cs:video_mode, 7
  394.         jz      d8_no_cp
  395.         call    pseudocursor            ; write block in xor
  396. d8_no_cp:
  397.         mov     ah, cur_attrib
  398.         mov     ds, f_cptr_seg          ; get segment of char ptr
  399.         assume  ds:nothing
  400.         cld                             ; make sure we'll increment
  401.  
  402.         ; Get a character, put it on the screen, repeat 'til end of line
  403.         ; or no more characters.
  404.         jcxz    f_loopdone              ; if count = 0, we're already done.
  405.         cmp     cs:escvector, 0         ; If in middle of an escape sequence,
  406.         jnz     f_in_escapex            ; jump to escape sequence handler.
  407.  
  408. f_tloop: ; If not in graphics mode, jump to alternate loop
  409.          ; What a massive kludge!  A better approach would have been
  410.          ; to collect characters for a "write n chars" routine
  411.          ; which would handle both text and graphics modes.
  412.         cmp     cs:video_mode,4
  413.         jb      f_t_cloop
  414.         cmp     cs:video_mode,7
  415.         jz      f_t_cloop
  416.  
  417.  
  418. f_g_cloop:
  419.         lodsb                           ; get char! (al = ds:[si++])
  420.         cmp     al,28                   ; is it a control char?
  421.         jb      f_control               ;  maybe...
  422. f_g_nctl:
  423.         call    putchar
  424.         dec     dx                      ; count down to end of line
  425.         loopnz  f_g_cloop               ; and go back for more.
  426.         jmp     short f_t_at_eol
  427.  
  428. f_t_cloop:
  429.         lodsb                           ; get char! (al = ds:[si++])
  430.         cmp     al,28                   ; is it a control char?
  431.         jb      f_control               ;  maybe...
  432. f_t_nctl:
  433.         stosw                           ; Put Char! (es:[di++] = ax)
  434.         dec     dx                      ; count down to end of line
  435.         loopnz  f_t_cloop               ; and go back for more.
  436.         jz      f_at_eol                ; at end of line; maybe do a crlf.
  437.         jmp     short f_loopdone
  438.  
  439. f_looploop:
  440. f_ansi_exit:                            ; in case we switched into
  441.         loopnz  f_tloop                 ; a graphics mode
  442. f_t_at_eol:
  443.         jz      f_at_eol
  444.  
  445. f_loopdone:
  446.  
  447.         ;--------- All done with write request -----------
  448.         ; DI is cursor address; cursor position in cur_y, dl.
  449.         mov     ax, cs
  450.         mov     ds, ax                  ; get our segment back
  451.         assume  ds:code
  452.  
  453.         ; Restore cur_x = max_x - dx + 1.
  454.         mov     al, max_x
  455.         inc     al
  456.         sub     al, dl
  457.         mov     cur_x, al
  458.         ; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
  459.         call    set_pseudocursor
  460.         ; Return to DOS.
  461.         xor     ax, ax                  ; No error, not busy.
  462.         ret
  463.  
  464.         ;---- handle control characters ----
  465.         ; Note: cur_x is not kept updated in memory, but can be
  466.         ; computed from max_x and dx.
  467.         ; Cur_y is kept updated in memory.
  468. f_control:
  469.         cmp     al,13                   ; carriage return?
  470.         jz      f_cr
  471.         cmp     al,10                   ; line feed?
  472.         jz      f_lf
  473.         cmp     al,27                   ; Is it an escape?
  474.         jz      f_escapex
  475.         cmp     al,8                    ; backspace?
  476.         jz      f_bs
  477.         cmp     al,9                    ; tab?
  478.         jz      f_tabx
  479.         cmp     al,7                    ; bell?
  480.         jz      f_bell
  481. f_not_ansi:                             ; not a control char
  482.         cmp     cs:video_mode, 4
  483.         jb      f_t_nctl
  484.         cmp     cs:video_mode, 7
  485.         jz      f_t_nctl
  486.         jmp     f_g_nctl
  487.  
  488. f_tabx: jmp     f_tab
  489. f_escapex:
  490.         jmp     f_escape
  491. f_in_escapex:
  492.         jmp     f_in_escape
  493.  
  494. f_bs:   ;----- Handle backspace -----------------
  495.         ; Moves cursor back one space without erasing.  No wraparound.
  496.         cmp     dl, cs:max_x            ; wrap around to previous line?
  497.         ja      fbs_wrap                ; yep; disallow it.
  498.         dec     di                      ; back up one char & attrib
  499.         dec     di
  500.         inc     dx                      ; and note one more char left on line.
  501. fbs_wrap:
  502.         jmp     f_looploop
  503.  
  504. f_bell: ;----- Handle bell ----------------------
  505.         call    beep
  506.         or      al, al                  ; clear z
  507.         jmp     f_looploop              ; Let main loop decrement cx.
  508.  
  509. f_cr:   ;----- Handle carriage return -----------
  510.         ; di -= cur_x<<1;               set di= address of start of line
  511.         ; dx=max_x+1;                   set bx= chars left in line
  512.         mov     al, cs:max_x
  513.         cbw
  514.         inc     ax
  515.         sub     al,dl                   ; Get cur_x into ax.
  516.         sub     di,ax
  517.         sub     di,ax
  518.         mov     dl,cs:max_x             ; Full line ahead of us.
  519.         inc     dx
  520.         mov     ah,cs:cur_attrib        ; restore current attribute
  521.         or      al,1                    ; clear z
  522.         jmp     f_looploop              ; and let main loop decrement cx
  523.  
  524. f_at_eol:
  525.         ;----- Handle overrunning right end of screen -------
  526.         ; cx++;                         compensate for double loop
  527.         ; if (!wrap_flag) { dx++; di-=2; }
  528.         ; else do_crlf;
  529.         inc     cx
  530.         test    cs:wrap_flag, 1
  531.         jnz     feol_wrap
  532.         dec     di
  533.         dec     di
  534.         inc     dx
  535.         jmp     f_looploop
  536. feol_wrap:
  537.         ; dx=max_x+1;                   set bx= chars left in line
  538.         ; di -= 2*(max_x+1);
  539.         ; do_lf
  540.         mov     dl, cs:max_x
  541.         inc     dx
  542.         sub     di, dx
  543.         sub     di, dx
  544.         ; fall thru to line feed routine
  545.  
  546. f_lf:   ;----- Handle line feed -----------------
  547.         ; if (cur_y >= max_y) scroll;           scroll screen up if needed
  548.         ; else { cur_y++; di += max_x<<1;       else increment Y
  549.  
  550.         mov     al,cs:Max_y
  551.         cmp     cs:Cur_y,al             ; do we need to scroll screen?
  552.         jae     flf_scroll              ;   yes, do it
  553.         inc     cs:cur_y
  554.         mov     al,cs:Max_x
  555.         cbw
  556.         inc     ax
  557.         shl     ax,1
  558.         add     di,ax
  559.         mov     ah,cs:cur_attrib        ; restore current attribute
  560.         or      al,1                    ; clear z
  561.         jmp     f_looploop              ; and let main loop decrement cx
  562. flf_scroll:
  563.         push    ax
  564.         push    bx
  565.         push    cx
  566.         push    dx
  567.         call    get_blank_attrib
  568.         mov     bh,ah                   ; color to use on new blank areas
  569.         mov     dl,cs:max_x
  570.         mov     dh,cs:max_y
  571.         cmp     cs:video_mode,4
  572.         jb      flf_scrollt
  573.         cmp     cs:video_mode,7
  574.         je      flf_scrollt
  575.         mov     al,1                    ; AL is number of lines to scroll.
  576.         mov     ah,6                    ; BIOS: scroll up
  577.         xor     cx,cx
  578.         int     10h                     ; call BIOS to scroll a rectangle.
  579. flf_scroll_done:
  580.         pop     dx
  581.         pop     cx
  582.         pop     bx
  583.         pop     ax
  584.         mov     ah,cs:cur_attrib        ; restore current attribute
  585.         or      al,1                    ; clear z
  586.         jmp     f_looploop              ; and let main loop decrement cx
  587. flf_scrollt:
  588.         push    es
  589.         push    ds
  590.         push    si
  591.         push    di
  592.         mov     ax,abs40
  593.         mov     ds,ax
  594.         assume  ds:abs40
  595.         mov     al,active_page
  596.         cbw
  597.         mov     cx,ax
  598.         xor     di,di                   ; starting video loc
  599.         mov     ax,0B000h               ; assume mono
  600.         cmp     cs:video_mode,7
  601.         je      flf_st_got
  602.         mov     ah,0B8h                 ; it's color
  603.         jcxz    flf_st_got
  604. flf_st_getpage:
  605.         add     di,4096                 ; move to active page
  606.         loop    flf_st_getpage
  607. flf_st_got:
  608.         mov     ds,ax                   ; set DS and ES to video segment
  609.         mov     es,ax
  610.         mov     si,di
  611.         mov     al,dh
  612.         inc     dx
  613.         mul     dl
  614.         mov     cx,ax                   ; CX is number of chars to move
  615.         xor     dh,dh
  616.         add     si,dx
  617.         add     si,dx                   ; SI points to one line below DI
  618.         rep     movsw                   ; scroll it
  619.         mov     cx,dx                   ; CX is chars per line
  620.         mov     ah,bh                   ; attribute
  621.         mov     al," "
  622.         rep     stosw                   ; clear the bottom line
  623.         pop     di
  624.         pop     si
  625.         pop     ds
  626.         pop     es
  627.         assume  ds:code
  628.         jmp     flf_scroll_done
  629.  
  630.  
  631. f_tab:  ;----- Handle tab expansion -------------
  632.         ; Get cur_x into al.
  633.         mov     al, cs:max_x
  634.         inc     al
  635.         sub     al, dl
  636.         ; Calculate number of spaces to output.
  637.         push    cx                      ; save cx
  638.         mov     cl, al                  ; get zero based x coordinate
  639.         and     cx,7
  640.         neg     cl
  641.         add     cl,8                    ; 0 -> 8, 1 -> 8, ... 7 -> 1
  642.         sub     dx, cx                  ; update chars-to-eol, maybe set z
  643.         pushf                           ; || save Z for main loop
  644.         ; ah is still current attribute.  Move CX spaces to the screen.
  645.         mov     al, ' '
  646.         cmp     cs:video_mode, 4
  647.         jb      F_SPC_MV
  648.         cmp     cs:video_mode, 7
  649.         jnz     f_tab_putc
  650. F_SPC_MV:
  651.         REP     STOSW
  652.         popf                            ; || restore Z flag for main loop test
  653.         pop     cx                      ; restore cx
  654.         jmp     f_looploop              ; Let main loop decrement cx.
  655.  
  656. ;--------------- graphics mode support -----------------------
  657.  
  658. f_tab_putc:     ; graphics mode- call putc to put the char
  659.         add     dx, cx                  ; move back to start of tab
  660. f_tp_lp:
  661.         call    putchar
  662.         dec     dx                      ; go to next cursor position
  663.         loop    f_tp_lp
  664.         popf                            ; Z set if wrapped around EOL
  665.         pop     cx
  666.         jmp     f_looploop
  667.  
  668.  
  669. ;---- putchar ------------------------------------------------
  670. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  671. ; On entry, registers set up as per xy_to_regs.
  672. ; Preserves all registers.
  673. putchar proc    near
  674.         push    dx
  675.         push    cx
  676.         push    bx
  677.         push    ax
  678.         ; 1. Set cursor position.
  679.         mov     al, cs:max_x
  680.         inc     al
  681.         sub     al,dl
  682.         mov     cs:cur_x, al
  683.         mov     dx, cs:cur_coords       ; get X & Y into DX
  684.         xor     bx, bx                  ; choose dpy page 0
  685.         mov     ah, 2                   ; chose "Set Cursor Position"
  686.         int     10h                     ; call ROM BIOS
  687.         ; 2. Write char & attribute.
  688.         mov     cx,1
  689.         pop     ax                      ; get char in AL
  690.         push    ax
  691.         mov     bl,ah                   ; attribute in BL
  692.         xor     bh,bh
  693.         mov     ah,9
  694.         int     10h
  695.         pop     ax
  696.         pop     bx
  697.         pop     cx
  698.         pop     dx
  699.         ret
  700. putchar endp
  701.  
  702. ;---- set_pseudocursor ------------
  703. ; If in graphics mode, set pseudocursor, else set real cursor.
  704. ; Destroys DS!!!!
  705.  
  706. set_pseudocursor        proc    near
  707.         cmp     cs:video_mode, 4
  708.         jb      SET_CURS
  709.         cmp     cs:video_mode, 7
  710.         jnz     pseudocursor
  711.  
  712. SET_CURS:    ; Write directly to 6845 cursor address register.
  713.         mov     bx,di
  714.         shr     bx,1                   ; convert word index to byte index
  715.  
  716.         mov     dx,Port_6845
  717.         mov     al,0Eh
  718.         out     dx,al
  719.  
  720.         jmp     $+2
  721.         inc     dx
  722.         mov     al, bh
  723.         out     dx, al
  724.  
  725.         jmp     $+2
  726.         dec     dx
  727.         mov     al, 0fh
  728.         out     dx, al
  729.  
  730.         jmp     $+2
  731.         inc     dx
  732.         mov     al, bl
  733.         out     dx, al
  734.  
  735.         ; Set cursor position in low memory.
  736.         assume  ds:abs40
  737.         mov     ax, abs40
  738.         mov     ds, ax
  739.         mov     ax, cs:cur_coords
  740.         mov     cursor_posn,ax
  741.         ret
  742.  
  743.         assume  ds:code
  744. set_pseudocursor        endp
  745.  
  746.  
  747. ;---- pseudocursor --------------------------------------------------
  748. ; Writes a color 15 block in XOR at the current cursor location.
  749. ; Preserves DS, ES, BX, CX, DX, SI, DI.
  750. ; Should be disableable- the pseudocursor slows down single-char
  751. ; writes by a factor of three.
  752. pseudocursor    proc    near
  753.         mov     ax, 8f16h       ; xor, color 15, ^V (small block)
  754.         call    putchar
  755.         ret
  756. pseudocursor    endp
  757.  
  758. ;--------------- end of graphics mode support --------------------
  759.  
  760. dosfn8  endp
  761.  
  762. ;--- get_blank_attrib ------------------------------------------------
  763. ; Determine new attribute and character for a new blank region.
  764. ; Use current attribute, just disallow blink and underline.
  765. ; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
  766. ; Returns result in AH, preserves all other registers.
  767. get_blank_attrib        proc    near
  768.         xor     ah,ah
  769.         cmp     cs:video_mode, 4
  770.         jb      GB_TX
  771.         cmp     cs:video_mode, 7
  772.         jnz     gb_aok                  ; if graphics mode, 0 is bkgnd
  773. GB_TX:
  774.         mov     ah, cs:cur_attrib
  775.         and     ah,7fh                  ; disallow blink
  776.         cmp     cs:video_mode,7         ; monochrome?
  777.         jne     gb_aok
  778.         cmp     ah,1                    ; underline?
  779.         jne     gb_aok
  780.         mov     ah,7                    ; yep- set it to normal.
  781. gb_aok: ret
  782. get_blank_attrib        endp
  783.  
  784.  
  785. ;---- searchbuf --------------------------------------------
  786. ; Called by getchar and peekchar to see if any characters are
  787. ; waiting to be gotten from sources other than BIOS.
  788. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  789. searchbuf       proc    near
  790.         ; Search the stuffahead buffers.
  791.         mov     cx,4                    ; number of buffers to check for chars
  792.         mov     bx,offset fnkey -4
  793. sbloop: add     bx,4                    ; point to next buffer record
  794.         mov     si,[bx].len
  795.         or      si,si                   ; empty?
  796.         loopz   sbloop                  ; if so, loop.
  797.         ret
  798. searchbuf       endp
  799.  
  800. ;---- getchar -----------------------------------------------
  801. ; Returns AL = next char.
  802. ; Trashes AX, BX, CX, BP, SI.
  803. getchar proc    near
  804. gc_searchbuf:
  805.         ; See if any chars are waiting in stuffahead buffers.
  806.         call    searchbuf
  807.         jz      gc_trykbd               ; No chars?  Try the keyboard.
  808.         ; A nonempty buffer was found.
  809.         dec     [bx].len
  810.         dec     si
  811.         mov     bp, [bx].adr            ; get pointer to string
  812.         mov     al, byte ptr ds:[bp][si]; get the char
  813.         ; Recognize function key sequences, move them to highest priority
  814.         ; queue.
  815.         sub     si,1                    ; set carry if si=0
  816.         jc      gc_nofnkey              ; no chars left -> nothing to protect.
  817.         cmp     bx, offset fnkey
  818.         jz      gc_nofnkey              ; already highest priority -> done.
  819.         or      al,al
  820.         jnz     gc_nofnkey              ; nonzero first byte -> not fnkey.
  821.         ; Found a function key; move it to highest priority queue.
  822.         dec     [bx].len
  823.         mov     ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  824. gc_fnkey:
  825.         mov     fnkey.len, 1
  826.         mov     fnkeybuf, ah            ; save it.
  827. gc_nofnkey:
  828.         ret                             ; Valid char in AL.  Return with it.
  829.  
  830. gc_trykbd:
  831.         xor     ah,ah
  832.         int     16h                     ; BIOS returns with char in AX
  833.         ; If it's Ctrl-break, it has already been taken care of.
  834.         or      ax, ax
  835.         jz      gc_trykbd
  836.  
  837. gcbark: or      al,al                   ; Is it a function key?
  838.         jz      gc_fnkey                ;   yep, special treatment
  839. gcdone: ret     ; with character in AL.
  840. getchar endp
  841.  
  842. ;---- peekchar -----------------------------------------------
  843. ; Returns Z if no character ready, AL=char otherwise.
  844. ; Trashes AX, BX, CX, BP, SI.
  845. peekchar        proc    near
  846. pc_searchbuf:
  847.         call    searchbuf
  848.         jz      pc_trykbd               ; No chars?  Try the keyboard.
  849.         ; A nonempty buffer was found.
  850.         dec     si
  851.         mov     bp,[bx].adr             ; get pointer to string
  852.         mov     al,byte ptr ds:[bp][si] ; get the char
  853.         ; Valid char from buffer in AL.  Return with it.
  854.         jmp     short pcdone
  855.  
  856. pc_brk: int     16h                     ; get rid of control-break in buffer
  857.  
  858. pc_trykbd:
  859.         mov     ah,1
  860.         int     16h                     ; BIOS returns with char in AX
  861.         jz      pcexit
  862.         or      ax,ax
  863.         jz      pc_brk  ; If ctl-brk, it's already been taken care of- kill it.
  864.  
  865. pcdone: or      ah,1                    ; NZ; char ready!
  866. pcexit: ret     ; with character in AL, Z true if no char waiting.
  867. peekchar        endp
  868.  
  869. ;---- beep ------------------------------------------------------
  870. ; Beep speaker; period given by beep_div, duration by beep_len.
  871. ; Preserves all registers.
  872.  
  873. beep_div        dw      1300            ; fairly close to IBM beep
  874. beep_len        dw      3               ; 3/18 sec- shorter than IBM
  875.  
  876. beep    proc    near
  877.         push    ax
  878.         push    cx
  879.         push    dx
  880.         push    bx
  881.         push    bp
  882.         push    si
  883.         push    di
  884.  
  885.         mov     al,10110110b            ; select 8253
  886.         mov     dx,43h                  ; control port address
  887.         out     dx,al
  888.         dec     dx                      ; timer 2 address
  889.         mov     ax, cs:beep_div
  890.         out     dx,al                   ; low byte of divisor
  891.         mov     al,ah
  892.         out     dx,al                   ; high byte of divisor
  893.         mov     dx,61h
  894.         in      al,dx                   ; get current value of control bits
  895.         push    ax
  896.         or      al, 3
  897.         out     dx,al                   ; turn speaker on
  898.  
  899.         ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  900.         push    es
  901.         mov     ax,abs40
  902.         mov     es,ax
  903.         assume  es:abs40
  904.         mov     bx,timer_low
  905.         add     bx,cs:beep_len
  906.         mov     cx, -1                  ; emergency, in case clock dead
  907.  
  908. beeplp: mov     ax, timer_low
  909.         cmp     ax,bx
  910.         loopne  beeplp
  911.  
  912.         pop     es
  913.         assume  es:code
  914.         pop     ax
  915.         and     al,0FCh                 ; turn speaker off
  916.         out     dx,al
  917.         pop     di
  918.         pop     si
  919.         pop     bp
  920.         pop     bx
  921.         pop     dx
  922.         pop     cx
  923.         pop     ax
  924.         ret
  925. beep    endp
  926.  
  927. CODE    ends
  928.         end
  929.